package luajitter

//#cgo CFLAGS: -I${SRCDIR}/include
//#cgo CFLAGS: -I${SRCDIR}/
//#cgo LDFLAGS: -L${SRCDIR}/lib/win_x86_64 -lluajit
//#include "go_luajit.h"
import "C"
import (
	"errors"
	"fmt"
	"unsafe"
)

var vmMap = make(map[*C.lua_State]*LuaState)

type LuaState struct {
	_l *C.lua_State
}

func NewState() *LuaState {
	vm := C.new_luajit_state()
	state := &LuaState{
		_l: vm,
	}
	vmMap[vm] = state
	return state
}

func (s *LuaState) Close() error {
	delete(vmMap, s._l)
	C.close_lua(s._l)
	s._l = nil // 确保指针被置为 nil
	return nil
}

func (s *LuaState) GetGcCount() (int, error) {
	if s._l == nil {
		return 0, errors.New("LuaState is closed")
	}
	return int(C.get_lua_memory_usage(s._l)), nil
}

func (s *LuaState) DoString(doString string) error {
	script := C.CString(doString)
	defer C.free(unsafe.Pointer(script))

	cErr := C.internal_dostring(s._l, script)

	defer C.free_lua_error(cErr)
	return LuaErrorToGo(cErr)
}

func (s *LuaState) getGlobal(path string, createIntermediateTables bool) (interface{}, error) {
	cPath := C.CString(path)
	defer C.free(unsafe.Pointer(cPath))

	cResult := C.get_global(s._l, cPath, (C._Bool)(createIntermediateTables))
	defer C.free_lua_error(cResult.err)

	err := LuaErrorToGo(cResult.err)
	var result interface{}
	if cResult.value != nil {
		valArray := (*[1 << 30]*C.struct_lua_value)(unsafe.Pointer(&cResult.value))
		vals := buildGoValues(s, 1, valArray)
		if len(vals) > 0 {
			result = vals[0]
			if valArray[0] != nil {
				C.free_temporary_lua_value(s._l, valArray[0])
			}
		}
	}

	return result, err
}

func (s *LuaState) GetGlobal(path string) (interface{}, error) {
	return s.getGlobal(path, false)
}

func (s *LuaState) setGlobal(path string, value interface{}, createIntermediateTables bool) error {
	cPath := C.CString(path)
	defer C.free(unsafe.Pointer(cPath))

	cValue, err := fromGoValue(s, value, nil)
	if err != nil {
		return err
	}
	if cValue.temporary == C._Bool(true) {
		defer C.free_temporary_lua_value(s._l, cValue)
	}

	cErr := C.set_global(s._l, cPath, cValue, (C._Bool)(createIntermediateTables))
	defer C.free_lua_error(cErr)

	return LuaErrorToGo(cErr)
}

func (s *LuaState) SetGlobal(path string, value interface{}) error {
	return s.setGlobal(path, value, false)
}

func (s *LuaState) InitGlobal(path string, value interface{}) error {
	return s.setGlobal(path, value, true)
}

// DoFile 读取并执行 Lua 脚本，同时保留源文件信息
func (s *LuaState) DoFile(doString string) error {
	script := C.CString(doString)
	defer C.free(unsafe.Pointer(script))

	cErr := C.internal_dofile(s._l, script)

	defer C.free_lua_error(cErr)
	return LuaErrorToGo(cErr)
}

func (s *LuaState) LoadBuffer(buf []byte, name string) error {
	// 将 []byte 转换为 C 字符串
	cBuf := C.CString(string(buf))     // 将 buf 转换为 C 字符串
	defer C.free(unsafe.Pointer(cBuf)) // 确保释放内存

	cName := C.CString(name)            // 将 name 转换为 C 字符串
	defer C.free(unsafe.Pointer(cName)) // 确保释放内存

	// 使用 len(buf) 获取字节数组的长度
	//cErr := C.luaL_dobuffer(s._l, cBuf, C.size_t(len(buf)), cName)
	//cErr := C.luaL_loadbuffer(s._l, cBuf, C.size_t(len(buf)), cName) || C.lua_pcall(s._l, 0, 0, 0)
	cErr := C.luaL_loadbuffer(s._l, cBuf, C.size_t(len(buf)), cName)

	// 如果加载成功，调用 pcall
	if cErr == 0 { // 0 表示成功
		cErr = C.lua_pcall(s._l, 0, 0, 0)
	}
	// 返回错误转换
	return LuaErrorToGo1(int(cErr)) // 将 cErr 转换为 int
}
func LuaErrorToGo1(err int) error {
	if err != 0 {
		return fmt.Errorf("Lua error: %d", err)
	}
	return nil
}
